/*
 * \file TitledDataModel.cpp
 *
 * \date Created on: Jan 31, 2009
 * \author sarah
 */

#include "TitledDataModel.h"

#include <DndData/TitledData.h>

#include <QDebug>
#include <QTimer>

/*!
 * \brief A Qt ModelView implementation for titled data objects.
 * \details TitledData, ie Race, Power, can be simply displayed as a two-level
 * tree where the top level are category headings, with icons, and below each
 * heading are a number (1 to N) of entries, containing details for that
 * category.
 *
 * The class implements the QAbstractItemModel interface, and is intended for
 * display in QTreeView widgets.  Sub-classes should provide a constructor that
 * calls addCategory() to fill the category headings, then simply override the
 * method data(int category, int entry, int role).  This method is called to
 * provide the entries under each category.
 *
 * The indexes are flagged using the the inline methods idToEntry() and
 * idToCategory() with the models internal id as an argument.  Top level
 * category nodes have a Category number, but the entry is -1.  Bottom
 * level entry nodes have their parent's Category number and an appropriate
 * entry number.  For example the 4th category heading has Category == 4,
 * and Entry == -1.  The 3rd entry in the 4th category has Category == 4,
 * and Entry == 3.  The method modelSpecToId() provides the reverse conversion.
 */

/*!
 * \fn int idToEntry(qint64 id)
 * \brief Extract the entry number from the value given by QModelIndex::internalId().
 * \param id The model id to extract from.
 * \return int identifying the entry number, or -1 if no entry (a category node).
 */

/*!
 * \fn int idToCategory(qint64 id)
 * \brief Extract the category number from the value given by QModelIndex::internalId().
 * \param id The model id to extract from.
 * \return int identifying the category number.
 */

/*!
 * \fn qint64 modelSpecToId(int category, int entry)
 * \brief Build an id value suitable for constructing a QModelIndex from a node spec.
 * \param category the category row/index for the node.
 * \param entry the entry row/index for the node (-1 in for a category head).
 */

/*!
 * \brief Construct a new TitledDataModel object
 * \details Initializes the model, and fills the category information by
 * calling addCategory() once for each top level category item in tree.
 */
TitledDataModel::TitledDataModel(const TitledData *source, QObject *parent)
    : QAbstractItemModel(parent)
    , mSource(source)
    , mIcon(":/icons/icons/32x32/d10.png")
{
    if (mIcon.isNull())
        qWarning("Could not load icon resource");
    addCategory("Title");
    addCategory("Summary");
    addCategory("Description");
    addEntry(0, mSource->title());
    addEntry(1, mSource->summary());
    addEntry(2, mSource->description());
}

/*!
 * \brief Destroy the object, releasing any resources.
 */
TitledDataModel::~TitledDataModel()
{
}

/*!
 * \brief Clear the model, removing all data from it.
 */
void TitledDataModel::clear()
{
    mDetails.clear();
    mCategoryNames.clear();
}

/*!
 * \brief Add an entry to a category.
 * \details Used during initialization, this method adds the \a entryDetails and
 * the optional \a icon (if specified) to the \a category.
 * \param category to add the entry to.
 * \param entryDetails to add into the category.
 * \param icon optional icon to add to the entry.
 */
void TitledDataModel::addEntry(int category, const QString &entryDetails)
{
    Q_ASSERT(category < mDetails.size());
    mDetails[category] << entryDetails;
}

/*!
 * \override
 * \brief Build a new index structure for this model.
 * \details Given the supplied \a row, \a column and \a parent make a new index.
 */
QModelIndex TitledDataModel::index(int row, int column, const QModelIndex &parent) const
{
    // qDebug() << "TitledModel::index" << row << "x" << column
    //          << (parent.isValid() ? QString::number(parent.row()) : QString("root"));
    // top level category index
    if (!parent.isValid())
    {
        int id = TitledDataUtil::modelSpecToId(row, -1);
        // void *idptr = (void*)TitledDataUtil::modelSpecToId(row, -1);
        return createIndex(row, column, id);
    }

    // "parent" is a bottom level entry (leaf node) and has no children
    // implementing the hasChildren() method should mean this never occurs
    if (TitledDataUtil::idToEntry(parent.internalId()) != -1)
        return QModelIndex();

    int parentCategory = TitledDataUtil::idToCategory(parent.internalId());
    int id = TitledDataUtil::modelSpecToId(parentCategory, row);
    QModelIndex ix = createIndex(row, column, id);
    // qDebug() << "valid parent" << parent.row();
    return ix;
}

/*!
 * \override
 */
QModelIndex TitledDataModel::parent(const QModelIndex &index) const
{
    // qDebug() << "TitledModel::parent"
    //          << (index.isValid() ? QString::number(index.row()) : QString("root"));
    if (!index.isValid())
        return QModelIndex();
    int category = TitledDataUtil::idToCategory(index.internalId());
    int entry = TitledDataUtil::idToEntry(index.internalId());
    if (entry == -1) // top level category node has empty parent
        return QModelIndex();
    int id = TitledDataUtil::modelSpecToId(category, -1);
    return createIndex(category, 0, id);
}

/*!
 * \override
 */
int TitledDataModel::rowCount(const QModelIndex &parent) const
{
    if (!parent.isValid())
        return mCategoryNames.size();
    int category = TitledDataUtil::idToCategory(parent.internalId());
    if (TitledDataUtil::idToEntry(parent.internalId()) == -1)
    {
        return mDetails[category].count();
    }
    // implementing hasChildren() should mean this case never occurs
    return 0;
}

/*!
 * \override
 */
int TitledDataModel::columnCount(const QModelIndex &parent) const
{
    if (!parent.isValid())
        return 1;
    if (TitledDataUtil::idToEntry(parent.internalId()) == -1)
        return 1;
    // implementing hasChildren() should mean this case never occurs
    return 0;
}

/*!
 * \override
 */
QVariant TitledDataModel::data(const QModelIndex &index, int role) const
{
    int category = TitledDataUtil::idToCategory(index.internalId());
    int entry = TitledDataUtil::idToEntry(index.internalId());
    // qDebug() << "TitledDataModel::data - category:" << category
    //          << "entry:" << entry;
    if (entry == -1)
    {
        if (role == Qt::DisplayRole)
            return mCategoryNames[category];
        else if (role == Qt::DecorationRole)
            return mIcon;
        return QVariant();
    }
    return data(category, entry, role);
}

/*!
 * \brief Build the model by adding a new category heading.
 * \details Called during model initialization (since it does not call any of the
 * model update routines) this method adds a new top level "Category" node to the
 * tree, with \a categoryName.
 * \param categoryName name of the new category.
 */
void TitledDataModel::addCategory(const QString & categoryName)
{
    mCategoryNames << categoryName;
    QList<QVariant> theDetails;
    mDetails << theDetails;
}

/*!
 * \override
 */
bool TitledDataModel::hasChildren(const QModelIndex & parent) const
{
    if (!parent.isValid())  // top level has categories
        return true;
    if (TitledDataUtil::idToEntry(parent.internalId()) == -1)
    {
        int category = TitledDataUtil::idToCategory(parent.internalId());
        return (mDetails[category].count() > 0);
    }
    return false;
}

/*!
\brief Calculate a value for display in the view, by category and entry.
\details Given a \a category and \a entry, determine an appropriate value for
display in the view.  This method may be re-implemented in sub-classes to
provide data appropriate for the modelled item.
\return QVariant containing the data.
 */
QVariant TitledDataModel::data(int category, int entry, int role) const
{
    if (role == Qt::DisplayRole)
    {
        if (category >= mDetails.count())
        {
            qWarning("TitledDataModel::data - request overflowed category table");
            return QVariant("Overflow");
        }
        QList<QVariant> dets = mDetails[category];
        if (entry >= dets.count())
        {
            qWarning("TitledDataModel::data - request overflowed entries table");
            return QVariant("Overflow");
        }
        return dets[entry];
    }
    return QVariant();
}
